/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit.handlers;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getCovereds;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.Lifeline;

import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.sequence.sedi.edit.FrameEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.command.AnalyseBestCoveredCommand;
import com.cea.papyrus.diagram.sequence.sedi.edit.command.AnalyseCoveredByCommand;
import com.cea.papyrus.diagram.sequence.sedi.edit.command.AnalyseOwnerCommand;
import com.cea.papyrus.diagram.sequence.sedi.edit.command.DecideExecutionOccurrenceBoundsCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.command.CoverCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.command.LifelinePosition;
import com.cea.papyrus.diagram.sequence.sedi.model.command.SetConstraintCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.command.SetOwnerCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;

/**
 * Handles automatic layout of ExecutionOccurrences.
 * 
 * @author frankd@ifi.uio.no
 */
public class ExecutionSpecificationLayoutHandler extends InteractionFragmentLayoutHandler {

    @SuppressWarnings("unchecked")
    public Rectangle layout(Diagram diagram, ExecutionSpecification eo) {
        if (eo.getStart() == null // none of these cases should happen, but just in case
                || getCovereds(eo.getStart()).isEmpty() || getCovereds(eo).isEmpty())
            // TODO query figure for default position
            return new Rectangle(100, 100, 1, 1);
        else {
            Lifeline lifeline = getCovereds(eo).get(0);
            GraphNode lifelineView = ModelUtil.getGraphNode(lifeline, diagram);
            Rectangle lifeBounds =
                    new Rectangle(lifelineView.getPosition(), lifelineView.getSize());
            // TODO Query figure/EditPart for preferred position
            return new Rectangle(lifeBounds.getCenter().x, 100, 1, 1);
        }
    }

    @SuppressWarnings("unchecked")
    public Command[] getMoveCommands(final EditPart fragmentPart, final Rectangle suggestedBounds) {

        final GraphNode fragmentView = (GraphNode) fragmentPart.getModel();

        final ExecutionSpecification fragment =
                ModelUtil.<ExecutionSpecification> getSemantic(fragmentView);

        final AnalyseBestCoveredCommand analyseCoveredCmd = new AnalyseBestCoveredCommand();
        analyseCoveredCmd.setInteractionEditPart(
        		(FrameEditPart) fragmentPart.getParent());
        analyseCoveredCmd.setFragmentNode(fragmentView);
        analyseCoveredCmd.setBounds(suggestedBounds);

        final DecideExecutionOccurrenceBoundsCommand analyseBoundsCmd =
                new DecideExecutionOccurrenceBoundsCommand();
        analyseBoundsCmd.setAnyEditPart(fragmentPart);
        analyseBoundsCmd.setExecutionSpecification(ModelUtil
                .<ExecutionSpecification> getSemantic(fragmentView));
        analyseBoundsCmd.setHint(suggestedBounds);

        final SetConstraintCommand setBoundsCmd = new SetConstraintCommand();
        setBoundsCmd.setElement(fragmentView);
        // glue1: location from decideBoundsCmd

        final AnalyseCoveredByCommand analyseCoveredByCmd = new AnalyseCoveredByCommand();
        analyseCoveredByCmd.setEditPart(fragmentPart);

        final CoverCommand startCoverCmd = new CoverCommand();
        startCoverCmd.setFragment(fragment.getStart());

        final CoverCommand finishCoverCmd = new CoverCommand();
        finishCoverCmd.setFragment(fragment.getFinish());
        // glue1: covered from analyseCoveredCmd

    	if (!isDetachedFromLifeline()) {
    		
        	LifelinePosition coveredLifelinePosition = 
        		new LifelinePosition(fragment.getStart().getCovereds().get(0), 0);
        	List<LifelinePosition>  lifelinePositions = 
        		new ArrayList<LifelinePosition>();
        	lifelinePositions.add(coveredLifelinePosition);
        	
        	startCoverCmd.setCovered(lifelinePositions);
        	finishCoverCmd.setCovered(lifelinePositions);
    	}
    	
        final AnalyseOwnerCommand analyseOwnerCmd = new AnalyseOwnerCommand();
        analyseOwnerCmd.setAnyEditPart(fragmentPart);
        analyseOwnerCmd.setDiagram(LookForElement.getDiagram(fragmentView));
        // glue2: location from decideBoundsCmd

        final SetOwnerCommand setOwnerCmd = new SetOwnerCommand();
        final SetOwnerCommand setStartOwnerCmd = new SetOwnerCommand();
        final SetOwnerCommand setFinishOwnerCmd = new SetOwnerCommand();
        setOwnerCmd.setFragment(fragment);
        setStartOwnerCmd.setFragment(fragment.getStart());
        setFinishOwnerCmd.setFragment(fragment.getFinish());
        // glue3: owner

        final Command glue1 = new Command() {
            public void execute() {
                List<LifelinePosition> covered = analyseCoveredCmd.getCovered();
                assert !covered.isEmpty();
                Lifeline coveredLifeline = covered.get(0).getLifeline();
                analyseBoundsCmd.setLifeline(coveredLifeline);

                GraphNode coveredLifelineView =
                        ModelUtil.getGraphNode(coveredLifeline, LookForElement
                                .getDiagram(fragmentView));
                analyseCoveredByCmd.setLifelineView(coveredLifelineView);
            }
        };

        final Command glue2 = new Command() {
            public void execute() {
                setBoundsCmd.setBounds(analyseBoundsCmd.getPreferredBounds());
                analyseOwnerCmd.setLocation(analyseBoundsCmd.getPreferredBounds().getLocation());
            }
        };

        final Command glue3 = new Command() {
        	public void execute() {
        		
        		if (isDetachedFromLifeline()) {
        			final List<LifelinePosition> covered = analyseCoveredCmd.getCovered();
        			assert !covered.isEmpty();
        			Lifeline coveredLifeline = covered.get(0).getLifeline();

        			final List<InteractionFragment> coveredBy = analyseCoveredByCmd.getCoveredBy();
        			assert !coveredBy.isEmpty();

        			if (fragment.getStart() != null) {
        				List<LifelinePosition> startCoveredList = new ArrayList<LifelinePosition>(1);
        				startCoveredList.add(new LifelinePosition(coveredLifeline, coveredBy
        						.indexOf(fragment.getStart())));
        				startCoverCmd.setCovered(startCoveredList);
        			}
        			if (fragment.getFinish() != null) {
        				List<LifelinePosition> finishCoveredList = new ArrayList<LifelinePosition>(1);
        				finishCoveredList.add(new LifelinePosition(coveredLifeline, coveredBy
        						.indexOf(fragment.getFinish())));
        				finishCoverCmd.setCovered(finishCoveredList);
        			}
        			// analyseBoundsCmd.setLifeline(coveredLifeline);
        		}
        	}
        };

        final Command glue4 = new Command() {
            public void execute() {
                setOwnerCmd.setOwner(analyseOwnerCmd.getOwner());
                setStartOwnerCmd.setOwner(analyseOwnerCmd.getOwner());
                setFinishOwnerCmd.setOwner(analyseOwnerCmd.getOwner());
            }
        };

        List<Command> cmds = new ArrayList<Command>(14);
        cmds.add(analyseCoveredCmd);
        cmds.add(glue1);
        cmds.add(analyseBoundsCmd);
        cmds.add(glue2);
        cmds.add(setBoundsCmd);
        cmds.add(analyseCoveredByCmd);
        cmds.add(glue3);
        if (fragment.getStart() != null)
            cmds.add(startCoverCmd);
        if (fragment.getFinish() != null)
            cmds.add(finishCoverCmd);
        cmds.add(analyseOwnerCmd);
        cmds.add(glue4);
        cmds.add(setOwnerCmd);
        if (fragment.getStart() != null)
            cmds.add(setStartOwnerCmd);
        if (fragment.getFinish() != null)
            cmds.add(setFinishOwnerCmd);

        Command[] cmdArray = new Command[cmds.size()];
        return cmds.toArray(cmdArray);
    }
}
